The Javascript string length is not suitable for measuring the
approximate length of a rendered text in the input method
indicator. Most single code point emoji like `🚀 U+1F680` get length 2
already, adding a variation selector to make the emoji display in
black and white like `🚀︎ U+1F680 U+FE0E` makes the length 3. More
complicated emoji sequences may get much longer lengths even though
the still display as one character. For example `👩👩👧👧 U+1F469,
U+200D, U+1F469, U+200D, U+1F467, U+200D, U+1F467` gets length 11.
This is not only the case for emoji but also for other text, for
example the Korean jamo `각 U+1100, U+1161, U+11A8` display as only 1
glyph but the Javascript length is 3.
A better way to measure the approximate length of a rendered text is
to count the Unicode grapheme clusters in the text using
Intl.Segmenter. This counts all the examples above as 1 grapheme
cluster each.
There seem to be some bugs in the Intl.Segmenter, for example
`क्षि U+0915, U+094D, U+0937, U+093F` is counted as 2 grapheme clusters,
the correct result should be 1. But it is good enough to measure
the approximate length of the input method indicator and certainly
better than the Javascript string length.
Resolves: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/1026
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3753>
(cherry picked from commit
f2246442a0ade263a1f9e775f02818bbd2862ae8)
}
}
+ _getGraphemeClusters(text = '') {
+ const segmenter = new Intl.Segmenter(undefined, {granularity: 'grapheme'});
+ const segments = [...segmenter.segment(text)].map(o => o.segment);
+ return segments;
+ }
+
_buildPropSubMenu(menu, props) {
if (!props)
return;
let currentSource = this._inputSourceManager.currentSource;
if (currentSource) {
let indicatorLabel = this._indicatorLabels[currentSource.index];
- if (text && text.length > 0 && text.length < 3)
+ const graphemeClusters = this._getGraphemeClusters(text);
+ if (graphemeClusters.length > 0 && graphemeClusters.length < 3)
indicatorLabel.set_text(text);
}
}